#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>

// version 1
namespace BlockQueueModule
{
    static const int gcap = 10;
    template <typename T>
    class BlockQueue
    {
    private:
        bool IsFull() { return _q.size() == _cap; };
        bool IsEmpty() { return _q.empty(); };

    public:
        BlockQueue(int cap = gcap) : _cap(cap), _cwait_num(0), _pwait_num(0)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_productor_cond, nullptr);
            pthread_cond_init(&_consumer_cond, nullptr);
        }

        void Equeue(const T &in) // 生产者
        {
            pthread_mutex_lock(&_mutex);
            while (IsFull())
            {
                std::cout << "生产者进入等待..." << std::endl;
                // 等待wait的第二个参数是醒后释放锁
                _pwait_num++;
                pthread_cond_wait(&_productor_cond, &_mutex); // wait的时候，必定是持有锁的！！是有问题的！
                _pwait_num--;
                // 返回，线程被唤醒&&重新申请并持有锁(它会在临界区内醒来！)
                std::cout << "生产者被唤醒..." << std::endl;
            }

            _q.push(in); // 生产
            // 肯定有数据
            if (_cwait_num)
            {
                std::cout << "叫醒消费者" << std::endl;
                pthread_cond_signal(&_consumer_cond);
            }
            pthread_mutex_unlock(&_mutex);
        }

        void Pop(T *out)
        {
            pthread_mutex_lock(&_mutex);
            while (IsEmpty())
            {
                std::cout << "消费者进入等待..." << std::endl;
                _cwait_num++;
                pthread_cond_wait(&_consumer_cond, &_mutex);
                _cwait_num--;
                std::cout << "消费者被唤醒..." << std::endl;
            }
            *out = _q.front();
            _q.pop();

            // 肯定有空间
            if (_pwait_num)
            {
                std::cout << "叫醒生产者" << std::endl;
                pthread_cond_signal(&_productor_cond);
            }

            pthread_mutex_unlock(&_mutex);
        }

        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_productor_cond);
            pthread_cond_destroy(&_consumer_cond);
        }

    private:
        std::queue<T> _q;               // 临界资源
        int _cap;                       // bq最大容量
        pthread_mutex_t _mutex;         // 互斥
        pthread_cond_t _productor_cond; // 生产者条件变量
        pthread_cond_t _consumer_cond;  // 消费者条件变量
        int _cwait_num;
        int _pwait_num;
    };
}
